package cn.walls1717.juc.cas;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 自旋锁测试<br>
 *
 * 自旋有两大缺点：<br>
 * 1.自旋时间长，造成cpu空转，浪费性能。<br>
 * 2.可能出现ABA问题，也就是说，线程1期望值是A，这时已经获取到A了，正在进行操作。这时线程2进来了
 * 在进行了一系列操作之后，又将值改为了A。线程1操作完后对比还是A，但是其实内容已经被操作过了。
 *
 * @author walls1717
 * @create 2022/12/12 13:34
 **/
@Slf4j
public class SpinLockDemo {

    /**
     * 使用原子类实现自旋锁，原子类底层使用了CAS机制。
     */
    static AtomicReference<Thread> atomicReference = new AtomicReference<>();

    /**
     * 加锁
     */
    private static void lock() {
        log.info("come in ...");
        // 获取当前线程
        Thread thread = Thread.currentThread();
        /*
         期望值是null，修改成当前线程。
         第一次加锁时，肯定是null，将值修改为当前线程。
         cas方法返回true，修改成功，这时候使用！取反，并不会进入循环方法。

         这时候第二个线程进来，期望值还是null，但是这时线程1已经将值修改为了线程1自身，
         这时候cas方法返回false表示修改失败，再经过！取反变成true，进入while循环，
         直到线程1调用unlock方法，将值修改为null，线程2才能cas成功。
         */
        while (!atomicReference.compareAndSet(null, thread)) {

        }
    }

    /**
     * 解锁
     */
    private static void unlock() {
        Thread thread = Thread.currentThread();
        // 期望值是当前线程，修改为null。配合lock方法使用。
        atomicReference.compareAndSet(thread, null);
        log.info("unlock ...");
    }

    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            lock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            unlock();
        }, "A").start();

        TimeUnit.MILLISECONDS.sleep(200);

        new Thread(()->{
            lock();
            log.info("业务");
            unlock();
        }, "B").start();
    }
}
